package uk.ac.rothamsted.utils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import uk.ac.rothamsted.BlastData;

public class OrthologCluster implements Serializable, Comparable<OrthologCluster>{
	/**
	 * 
	 */
	private static final long serialVersionUID = -5665607334440833482L;
	private final Set<String> members = new HashSet<String>();
	private final OrthologClusterSet parentSet;
	private final Set<OrthologCluster> children = new HashSet<OrthologCluster>();
	private BlastData mainPair;
	private double score;
	private boolean delete = false;
	private boolean isChild = false;
	private boolean parent = false;

	OrthologCluster(BlastData mainPair, OrthologClusterSet parentSet){
		this.parentSet = parentSet;
		this.score = mainPair.score;
		this.mainPair = mainPair;
		parentSet.setMembership(mainPair.id1, this);
		parentSet.setMembership(mainPair.id2, this);
		members.add(mainPair.id1);
		members.add(mainPair.id2);
	}

	public void add(BlastData bd){
		if(!((mainPair.id1.equals(bd.id1))||(mainPair.id2.equals(bd.id1)))){
			members.add(bd.id1);
			parentSet.setMembership(bd.id1, this);
		}
		if(!((mainPair.id1.equals(bd.id2))||(mainPair.id2.equals(bd.id2)))){
			members.add(bd.id2);
			parentSet.setMembership(bd.id2, this);
		}
	}

	public void add(String id){
		if(((mainPair.id1.equals(id))||(mainPair.id2.equals(id)))){
			members.add(id);
			parentSet.setMembership(id, this);
		}
	}

	public void remove(String id){
		if(id.equals(mainPair.id1) || id.equals(mainPair.id2)){
			throw new RuntimeException("Attempting to delete a member of the main ortholog pair - delete this cluster instead.");
		}
		members.remove(id);
		parentSet.removeMembership(id, this);
	}

	public void addAll(Collection<String> ids){
		for(String id : ids){
			this.add(id);
		}
	}

	public void removeAll(Collection<String> ids) {
		for(String id : ids){
			this.remove(id);
		}
	}

	public double getScore(){
		return score;
	}

	public BlastData getMainPair(){
		return mainPair;
	}

	public boolean contains(String id){
		return members.contains(id);
	}

	public final void setChild(){
		isChild = true;
	}

	public final void setToMerge(OrthologCluster cluster){
		children.add(cluster);
		cluster.setChild();
		parent = true;
	}

	public final void setToDelete(){
		delete = true;
	}

	public boolean getIsChild(){
		return isChild;
	}

	public Set<OrthologCluster> getChildren(){
		return children;
	}

	public boolean getDelete(){
		return delete;
	}

	public boolean getParent(){
		return parent;
	}

	public void setParent(){
		parent = false;
	}

	public final void merge(OrthologCluster cluster){
		for(String paralog:cluster.members){
			if((paralog.equals(mainPair.id1))||(paralog.equals(mainPair.id2))){
				continue;
			}
			members.add(paralog);
		}

		if(!(cluster.mainPair.id1.equals(mainPair.id1))){

			members.add(cluster.mainPair.id1);
		}
		if(!(cluster.mainPair.id2.equals(mainPair.id2))){
			members.add(cluster.mainPair.id2);
		}

		for(String member : cluster.members){
			parentSet.removeMembership(member, cluster);
			parentSet.setMembership(member, this);
		}
		children.remove(cluster);
		cluster.delete();
	}

	final void delete(){
		if(parentSet.contains(this)){
			parentSet.deleteCluster(this);
		}
		members.clear();
		score = Double.NaN;
		mainPair = null;

	}

	public OrthologClusterSet getParentSet(){
		return parentSet;
	}

	public boolean isInvalid(){
		return mainPair == null;
	}

	public Set<String> getMembers(){
		return members;
	}

	public List<OrthologCluster> getOverlappingClusters(){
		Set<OrthologCluster> result = parentSet.getClustersForIds(this.members);
		result.remove(this);

		List<OrthologCluster> output = new ArrayList<OrthologCluster>(result);
		Collections.sort(output);
		return output;
	}



	public String print(int num){
		String output = "";

		for(String paralog : members){
			double score;
			if(getParentSet().getSpeciesId(paralog) == 0){
				if(getParentSet().getAllHits().get(parentSet.getSortedKey(paralog, mainPair.id1)) == null){
					continue;
				}
				score = (getParentSet().getAllHits().get(parentSet.getSortedKey(paralog, mainPair.id1))).score;
			}else{
				if(getParentSet().getAllHits().get(parentSet.getSortedKey(paralog, mainPair.id2)) == null){
					continue;
				}
				score = (getParentSet().getAllHits().get(parentSet.getSortedKey(paralog, mainPair.id2))).score;
			}

			output += "\n"  + mainPair.id1 + "\t" + mainPair.id2 + "\t" + mainPair.score + "\t" + num + "\t" + paralog + "\t" + score;
		}
		return output;
	}





	public int size(){
		return members.size();
	}

	@Override
	public String toString(){
		String output;
		output = mainPair.toString();
		return output;
	}


	@Override
	public int compareTo(OrthologCluster arg0) {
		if(this.getScore() > arg0.getScore()){
			return 1;
		}else if(this.getScore() < arg0.getScore()){
			return -1;
		}else{
			return 0;
		}
	}

	@Override
	public boolean equals(Object arg0){
		if(!(arg0 instanceof OrthologCluster)){
			return false;
		}
		return this.score == ((OrthologCluster)arg0).score;
	}



}
